﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DocumentManager.cs" company="Concept Architecture">
//   (c) 2010 Muhammad Umer Farooq
// </copyright>
// <summary>
//   Defines the DocumentManager type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Hisaab.Windows.Common
{
    using System;
    using System.Collections.ObjectModel;
    using System.Diagnostics.Contracts;

    /// <summary>
    /// Defines the <seealso cref="DocumentManager"/> type.
    /// </summary>
    public class DocumentManager
    {
        /// <summary>
        /// Collection of documents
        /// </summary>
        private readonly ObservableCollection<Document> _documents;

        /// <summary>
        /// Readonly documents
        /// </summary>
        private readonly ReadOnlyObservableCollection<Document> _readOnlydocuments;

        /// <summary>
        /// Static instance of the document manager
        /// </summary>
        private static readonly DocumentManager instance = new DocumentManager();

        /// <summary>
        /// Active document
        /// </summary>
        private Document _activeDocument;

        /// <summary>
        /// Prevents a default instance of the <see cref="DocumentManager"/> class from being created.
        /// </summary>
        private DocumentManager()
        {
            _documents = new ObservableCollection<Document>();
            _readOnlydocuments = new ReadOnlyObservableCollection<Document>(_documents);
        }

        /// <summary>
        /// Occurs when [active document changed].
        /// </summary>
        public event EventHandler<ActiveDocumentChangedEventArgs> ActiveDocumentChanged;

        /// <summary>
        /// Gets the instance.
        /// </summary>
        public static DocumentManager Instance
        {
            get
            {
                return instance;
            }
        }

        /// <summary>
        /// Gets or sets the active document.
        /// </summary>
        /// <value>
        /// The active document.
        /// </value>
        public Document ActiveDocument
        {
            get
            {
                return _activeDocument;
            }

            set
            {
                Contract.Ensures(ActiveDocument == value, "The active document property failed to actually set the active document");
                if (_activeDocument == value)
                {
                    return;
                }

                if (value == null && Documents.Count == 1)
                {
                    throw new InvalidOperationException("Cannot set the active document to null when there are still open documents");
                }

                var previouslyActiveDocument = _activeDocument;

                _activeDocument = value;

                if (value != null && !_documents.Contains(value))
                {
                    _documents.Add(value);
                }

                if (previouslyActiveDocument != null)
                {
                    previouslyActiveDocument.LostActivation();
                }

                if (value != null)
                {
                    value.Activated();
                }

                if (ActiveDocumentChanged != null)
                {
                    var eventArgs = new ActiveDocumentChangedEventArgs(previouslyActiveDocument, value);
                    ActiveDocumentChanged(this, eventArgs);
                }
            }
        }

        /// <summary>
        /// Gets the count.
        /// </summary>
        public int Count
        {
            get
            {
                return _documents.Count;
            }
        }

        /// <summary>
        /// Gets the documents.
        /// </summary>
        public ReadOnlyObservableCollection<Document> Documents
        {
            get
            {
                Contract.Ensures(Contract.Result<ReadOnlyObservableCollection<Document>>() != null, "The Documents property should never return null");
                return _readOnlydocuments;
            }
        }

        /// <summary>
        /// Opens the specified document.
        /// </summary>
        /// <param name="document">The document.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Contracts", "Ensures", Justification = "Nothing")]
        public void Open(Document document)
        {
            Contract.Requires(document != null, "The new document must not be null.");
            Contract.Ensures(Documents.Contains(document), "After opening a document it should be part of the open documents collection");
            if (Documents.Contains(document))
            {
                return;
            }

            _documents.Add(document);

            if (_documents.Count == 1)
            {
                ActiveDocument = document;
            }
        }

        /// <summary>
        /// Closes the current.
        /// </summary>
        public void CloseCurrent()
        {
            if (ActiveDocument != null)
            {
                Close(ActiveDocument);
            }
        }

        /// <summary>
        /// Closes the specified document.
        /// </summary>
        /// <param name="document">The document.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Contracts", "Ensures", Justification = "Nothing")]
        public void Close(Document document)
        {
            Contract.Requires(document != null, "The document to close must not be null");
            Contract.Ensures(Documents.Contains(document) == false, "After closing a document it should not be part of the open documents collection");
            Contract.Ensures(ActiveDocument != document, "After closing a document it cannot be the active document");

            var wasClosed = _documents.Remove(document);

            if (ActiveDocument == document)
            {
                var newlyActiveDocument = Documents.Count == 0 ? null : Documents[0];
                ActiveDocument = newlyActiveDocument;
            }

            if (wasClosed)
            {
                document.DocumentClosed();
            }
        }

        /// <summary>
        /// Objects the invariants.
        /// </summary>
        [ContractInvariantMethod]
        private void ObjectInvariants()
        {
            Contract.Invariant(_documents != null, "The internal writable collection of documents must never be null");
            Contract.Invariant(_readOnlydocuments != null, "The read-only collection of documents must never be null");
            Contract.Invariant(ActiveDocument == null || _documents.Contains(ActiveDocument), "Either there are no documents or the active document is open");
        }
    }
}